using System;
using System.Drawing;
using System.Windows.Forms; 

namespace Linquid
{
	/// <summary>
	/// Dialog built by combining layout produced by an UI action 
	/// with OK and Cancel buttons.
	/// </summary>
	public class Dialog<A>
	{
		private Form dialog;
		
		/// <summary>
		/// Result of the dialog. Only valid if the Show method returned true.
		/// </summary>
		public A Result {get; private set;}
		
		/// <summary>
		/// Creates an OK/Cancel dialog from an UI action.
		/// </summary>
		/// <param name="ui">Action to use.</param>
		public Dialog(UI<Source<A>> ui) : 
			this(ui, LayoutDirection.FromTop) {}
		
		/// <summary>
		/// Creates an OK/Cancel dialog from an UI action.
		/// </summary>
		/// <param name="ui">Action to use.</param>
		/// <param name="layout">
		/// Defines where to create OK/Cancel buttons relative to dialog's content.
		/// </param>
		public Dialog(UI<Source<A>> ui, LayoutDirection layout) : 
			this(form => ui, layout) {}
		
		/// <summary>
		/// Creates an OK/Cancel dialog from an UI action.
		/// </summary>
		/// <param name="f">Function returning an UI action given the
		/// dialog's form. Can be used to set the form properties before 
		/// applying the layout.</param>		
		public Dialog(Func<Form, UI<Source<A>>> f):
			this(f, LayoutDirection.FromTop) {}
				
		/// <summary>
		/// Creates an OK/Cancel dialog from an UI action.
		/// </summary>
		/// <param name="f">Function returning an UI action given the
		/// dialog's form. Can be used to set the form properties before 
		/// applying the layout.</param>
		/// <param name="layout">
		/// Defines where to create OK/Cancel buttons relative to dialog's content.
		/// </param>
		public Dialog(Func<Form, UI<Source<A>>> f, LayoutDirection layout)
		{
			dialog = new Form()
			{				
				MaximizeBox = false,
				MinimizeBox = false,
				FormBorderStyle = FormBorderStyle.FixedDialog
			};			
			
			var ui = f(dialog);
			
			var dialogUI =			
				from value in ui
				from _ in Padding(layout, dialog.Margin)
			 	from click in AppendButtons(layout)
				orderby layout
				let result =
					from x in value
					from c in click
					orderby click
					select new {x, c}
				orderby layout
 			 	select result;
				
			dialogUI.Run(dialog).ValueChanged += item =>
			{			
				if (item.c)
				{
					Result = item.x;
					dialog.DialogResult = DialogResult.OK;
				}
				else
				{
					dialog.DialogResult = DialogResult.Cancel;
				}
				
				dialog.Close();
			};
		}				
		
		private UI<Nothing> Padding(LayoutDirection layout, Padding margins)
		{
			return 
				UIM.EmptySpace(
					(layout == LayoutDirection.FromLeft || layout == LayoutDirection.FromRight) ?
							new Size(margins.Right, 0) :
							new Size(0, margins.Bottom),
						0.0f, 0.0f);
		}
		
		private UI<Source<bool>> AppendButtons(LayoutDirection layout)
		{
			
			return 				
				(layout == LayoutDirection.FromLeft || layout == LayoutDirection.FromRight) ?
					from okClick in UIM.Button("Ok", 0.0f, 0.0f)
					from cancelClick in UIM.Button("Cancel", 0.0f, 0.0f)				
					from _ in UIM.EmptySpace(0.0f, 1.0f)
					orderby LayoutDirection.FromTop
					select okClick.Const(true).Join(
						cancelClick.Const(false)) :
								
					from _ in UIM.EmptySpace(1.0f, 0.0f)
					from okClick in UIM.Button("Ok", 0.0f, 0.0f)
					from cancelClick in UIM.Button("Cancel", 0.0f, 0.0f)				
					orderby LayoutDirection.FromLeft
					select okClick.Const(true).Join(
					   cancelClick.Const(false));								
		}	
		
		/// <summary>
		/// Shows the dialog
		/// </summary>
		/// <returns>true if OK was clicked, and false otherwise</returns>
		public bool Show()
		{
			return dialog.ShowDialog() == DialogResult.OK;
		}
	}
}